using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.Win32;
using WowAceUpdater.libzip.Zip;
using WowAceUpdater.Properties;

namespace WowAceUpdater
{
    internal static class WowEnvironment
    {
        #region Enums

        // Assumes IsInstalled() = true
        internal enum PackOperation
        {
            UNPACK = 1,
            PACK = 2,
        } ;

        #endregion

        #region Constants

        private const string OLD_ADDON_SOURCE_INFO_FILE = ".source";

        private const string ADDON_SOURCE_INFO_FILE = "source.wau";

        private const string NOPACK_TRAILER = ".nopack";

        private const string NOUNPACK_TRAILER = ".nounpack";

        private const string ZIP_INVENTORY_FILE = "ziplist.wau";

        #endregion

        #region Fields

        private static SortedDictionary<string, rssAddonItem> _LocalLookup;

        private static readonly XmlReaderSettings _xmlReaderSettings = new XmlReaderSettings();

        private const int MAX_BACKUPS_TO_KEEP = 10;
        private static string _AddonsDirectory;

        private static string _BackupDirectory;

        private static FileInfo _LastBackupFile;

        #endregion

        #region Constructors

        static WowEnvironment()
        {
            _xmlReaderSettings.ConformanceLevel = ConformanceLevel.Fragment;
            _xmlReaderSettings.IgnoreWhitespace = true;
            _xmlReaderSettings.IgnoreComments = true;
        }

        #endregion

        #region Properties

        private static readonly object oLookLock = new object();

        public static string AddonsDirectory
        {
            get
            {
                if (_AddonsDirectory == null)
                    AddonsDirectory = 
                        Path.Combine(
                            Path.Combine(FileSystem.CheckPathString(Settings.Default.WowInstallDirectory), "Interface"),
                            "Addons");

                return _AddonsDirectory;
            }
            set
            {
                _AddonsDirectory = FileSystem.CheckPathString(value);
                if (string.IsNullOrEmpty(_AddonsDirectory))
                    BackupDirectory = null;
                else
                    FileSystem.EnsureDirExists(_AddonsDirectory);
                
            }
        }

        public static string BackupDirectory
        {
            get
            {
                if (string.IsNullOrEmpty(_BackupDirectory))
                    _BackupDirectory =
                        Path.Combine(Settings.Default.WowInstallDirectory, "WauBackups");
                return _BackupDirectory;
            }
            set { _BackupDirectory = value; }
        }

        public static FileInfo LastBackupFile
        {
            get
            {
                if (_LastBackupFile == null)
                    _LastBackupFile = new FileInfo(
                        Path.Combine(BackupDirectory,
                                     string.Format("backup{0:000}.xml.gz", 0)));
                return _LastBackupFile;
            }
            set { _LastBackupFile = value; }
        }


        private static SortedDictionary<string, rssAddonItem> LocalLookup
        {
            get
            {
                lock (oLookLock)
                {
                    if (_LocalLookup == null)
                    {
                        _LocalLookup = new SortedDictionary<string, rssAddonItem>();
                        foreach (string d in Directory.GetDirectories(AddonsDirectory))
                            SetLocalAddonSource(d, GetAddonObject(d));
                    }
                }
                return _LocalLookup;
            }
            /*
                        set { lock (oLookLock) WowEnvironment._LocalLookup = value; }
            */
        }

        internal static void SetLocalAddonSource(string s, rssAddonItem i)
        {
            lock (oLookLock)
                LocalLookup[s] = i;
        }

        #endregion

        #region Internal Methods

        private static XmlSerializer _serializerAddonItem;
        private static SortedDictionary<string, rssAddonItem> _providerList;
        // Duplicated code from wowacefileserver
        public static SortedDictionary<string, rssAddonItem> LocalProviderAddon
        {
            get
            {
                if (_providerList == null)
                {
                    Program.LogDbg("Rebuilding Local Providers");
                    _providerList = new SortedDictionary<string, rssAddonItem>();

                    lock (oLookLock) 
                        foreach (rssAddonItem addon in LocalLookup.Values)
                    {
                        if (addon == null)
                            continue;

                        if (addon.provides != null)
                            foreach (string node in addon.provides)
                                if (!_providerList.ContainsKey(node))
                                    _providerList.Add(node, addon);
                    }
                }

                return _providerList;
            }
            set { _providerList = value; }
        }

        internal static string BackupAddonConfiguration()
        {
            string[] addons = Directory.GetDirectories(AddonsDirectory);

            StringBuilder sb = new StringBuilder();

            foreach (string addonDir in addons)
            {
                string addonSource = Path.Combine(addonDir, ADDON_SOURCE_INFO_FILE);
                if (File.Exists(addonSource))
                {
                    String s = File.ReadAllText(addonSource); 
                    const string enc = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
                    if (!string.IsNullOrEmpty(s))
                    {
                        sb.Append(s.Replace(enc, string.Empty));
                    }
                }
            }

            string strBackupDir = BackupDirectory;
            FileSystem.EnsureDirExists(BackupDirectory);
            int backupCount = Directory.GetFiles(strBackupDir, "backup???.xml.gz").Length;

            string strFileName;
            if (backupCount >= MAX_BACKUPS_TO_KEEP)
            {
                strFileName = string.Format("backup{0:000}.xml.gz", MAX_BACKUPS_TO_KEEP);
                File.Delete(Path.Combine(strBackupDir, strFileName));
                backupCount = MAX_BACKUPS_TO_KEEP - 1;
            }

            for (int i = backupCount; i >= 0; i--)
            {
                strFileName = string.Format("backup{0:000}.xml.gz", i);
                string strFileName2 = string.Format("backup{0:000}.xml.gz", i + 1);
                if (File.Exists(Path.Combine(strBackupDir, strFileName)))
                    File.Move(Path.Combine(strBackupDir, strFileName),
                              Path.Combine(strBackupDir, strFileName2));
            }

            strFileName = string.Format("backup{0:000}.xml.gz", 0);
            File.Delete(Path.Combine(strBackupDir, strFileName));

            StreamWriter sw = new StreamWriter(Path.Combine(strBackupDir, strFileName));
            Stream gzi = Compression.OpenGZipOutputStream(sw.BaseStream);
            XmlWriter xw = new XmlTextWriter(gzi, Encoding.UTF8);

            using (xw)
            {
                xw.WriteStartDocument();
                xw.WriteStartElement("wau");
                xw.WriteRaw(sb.ToString());
                xw.WriteEndElement();
                xw.WriteEndDocument();
            }

            return Path.Combine(strBackupDir, strFileName);
        }

        internal static string GetAddonDirectory(string addonName)
        {
            return Path.Combine(AddonsDirectory, addonName);
        }

        internal static rssAddonItem GetAddonItem(string addonName)
        {
            rssAddonItem x = null;

            lock (oLookLock)
                if (LocalLookup.ContainsKey(addonName))
                    return LocalLookup[addonName];

            string addonDir = GetAddonDirectory(addonName);

            lock (addonName)
                if (Directory.Exists(addonDir))
                {
//                    string addonSource = Path.Combine(addonDir, ADDON_SOURCE_INFO_FILE);

                    x = GetAddonObject(addonName);
                }

            SetLocalAddonSource(addonName, x);

            return x;
        }


        private static rssAddonItem GetAddonObject(string addonName)
        {
            rssAddonItem item = null;
            string addonDir = GetAddonDirectory(addonName);
            string oldaddonSource = Path.Combine(addonDir, OLD_ADDON_SOURCE_INFO_FILE);
            string addonSource = Path.Combine(addonDir, ADDON_SOURCE_INFO_FILE);

            lock (addonName)
            {
                try
                {
                    // Upgrade to the new file name
                    if (File.Exists(oldaddonSource) && !File.Exists(addonSource))
                        File.Move(oldaddonSource, addonSource);

                    if (File.Exists(addonSource))
                        lock (addonName)
                        {
                            XmlSerializer serializer =
                                new XmlSerializer(typeof (rssAddonItem));

                            FileStream fs = new FileStream(addonSource, FileMode.Open);

                            using (fs)
                                item = (rssAddonItem) serializer.Deserialize(fs);
                        }
                }
                catch (Exception e)
                {
                    Program.LogDbg(e.ToString());
                }
            }
        
            return item;
        }


        internal static FileInfo BackupSavedVariables(TextWriter feedback)
        {
            DirectoryInfo WTF = new DirectoryInfo(
                Path.Combine(Settings.Default.WowInstallDirectory, "WTF"));

            DirectoryInfo Account = new DirectoryInfo(
                Path.Combine(WTF.FullName, "Account"));

            FileInfo BackupFile = new FileInfo(Path.Combine(WTF.FullName, "account-backup.zip"));
            FastZip account = new FastZip();
            account.CreateZip(BackupFile.FullName,
                              Account.FullName, true, "");

            return BackupFile;
        }

        internal static void CleanSavedVariables(TextWriter feedback)
        {
            DirectoryInfo Addons = new DirectoryInfo(AddonsDirectory);
            DirectoryInfo[] installeddirs = Addons.GetDirectories();

            List<string> installed = new List<string>(installeddirs.Length);
            foreach (DirectoryInfo d in installeddirs)
            {

                string resultString = null;
                try
                {
                    resultString = Regex.Match(d.Name, @"([\d\w-_!]*?)(?:-\d+\.\d*)", RegexOptions.IgnoreCase).Groups[1].Value;
                }
                catch (ArgumentException ex)
                {
                    Program.LogDbg(ex.ToString());
                }

                if (!string.IsNullOrEmpty(resultString))
                    installed.Add(resultString);

                installed.Add(d.Name);
            }

            installed.Add("SavedVariables");

            DirectoryInfo WTF = new DirectoryInfo(
                Path.Combine(Settings.Default.WowInstallDirectory, "WTF"));

            DirectoryInfo Account = new DirectoryInfo(Path.Combine(WTF.FullName, "Account"));

            int count = 0;
            long bytes = 0;
            foreach (DirectoryInfo d in Account.GetDirectories("*", SearchOption.AllDirectories))
            {
                feedback.WriteLine("Checking {0}", d.FullName);

                foreach (FileInfo f in d.GetFiles("*.*"))
                    if (f.Extension == ".lua" || f.Extension == ".bak")
                    {
                        string fname = Path.GetFileNameWithoutExtension(f.Name);
                        if (Path.GetExtension(fname) == ".lua")
                            fname = Path.GetFileNameWithoutExtension(fname);

                        if (!installed.Contains(fname))
                        {
                            feedback.WriteLine(string.Format(Resources.Removing, f.Name));
                            bytes += f.Length;
                            count++;
                            f.Delete();
                        }
                    }
            }

            feedback.WriteLine();
            feedback.WriteLine(Resources.SVCleanupFilesDeleted, count, new Decimal(bytes/1024.0));
        }

        internal static VersionNumber GetAddonRevision(string addonName)
        {
            lock (addonName)
                return _GetAddonRevision(addonName);
        }

        internal static XmlDocument GetAddonSource(string addonName)
        {
            lock (addonName)
            {
                XmlDocument x = new XmlDocument();
                string addonDir = GetAddonDirectory(addonName);
                string addonSource = Path.Combine(addonDir, ADDON_SOURCE_INFO_FILE);
                if (Directory.Exists(addonDir) && File.Exists(addonSource))
                {
                    x.Load(addonSource);
                }

                return x;
            }
        }

        internal static string GetInstallFolder()
        {
            RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Blizzard Entertainment\World of Warcraft", false);
            if (key != null)
            {
                return key.GetValue("InstallPath", "") as string;
            }
            key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\wow6432node\Blizzard Entertainment\World of Warcraft", false);
            if(key != null)
                return key.GetValue("InstallPath", "") as string;

            return null;
        }

        internal static void InstallAddon(string zipFile, string addonName, rssAddonItem addonSource,
                                          TextWriter feedback, ICollection<string> list, Object oLock)
        {
            lock (addonName)
            {
                _InstallAddon(zipFile, addonName, addonSource, feedback, list, oLock);
            }
        }

        internal static bool IsInstalled(string addonName)
        {
            lock (addonName)
                return Directory.Exists(GetAddonDirectory(addonName));
        }

        internal static string ModulesFile(string addonName)
        {
            return Path.Combine(GetAddonDirectory(addonName), "modules.xml");
        }

        internal static string PartOfPackage(string p)
        {
            try
            {
                if (LocalProviderAddon.ContainsKey(p))
                {
                    string s = LocalProviderAddon[p].title;
                    Program.LogDbg(string.Format("{0} Part of {1}", p, s));
                    return s;
                }
            }
            catch (Exception e)
            {
                Program.LogDbg(e.ToString());
            }

            return null;
        }

        internal static void RepackAddon(string addonName, TextWriter feedback,
                                         ICollection<string> list,
            ICollection<string> zipfiles, Object oLock, bool bPreserveContents)
        {
            lock (addonName)
                _RepackAddon(addonName, feedback, list, zipfiles, oLock, bPreserveContents);
        }


        internal static void SaveAddonSource(rssAddonItem addonSource)
        {
            lock (addonSource.title)
            {
                using (
                    XmlWriter xw =
                        new XmlTextWriter(
                            Path.Combine(GetAddonDirectory(addonSource.title),
                                         ADDON_SOURCE_INFO_FILE),
                            Encoding.UTF8))
                {
                    if (_serializerAddonItem == null)
                        _serializerAddonItem = new XmlSerializer(typeof(rssAddonItem));
                    _serializerAddonItem.Serialize(xw, addonSource);
                }

                SetLocalAddonSource(addonSource.title, addonSource);
            }
        }

        internal static void SaveAddonSource(string addonName, XmlNode addonSource)
        {
            lock (addonName)
            {
                using (
                    XmlWriter xw =
                        new XmlTextWriter(
                            Path.Combine(GetAddonDirectory(addonName),
                                         ADDON_SOURCE_INFO_FILE),
                            Encoding.UTF8))
                    xw.WriteRaw(addonSource.OuterXml);

                rssAddonItem r = WowAceFilesServer.GetItemFromSource(addonSource);

                SetLocalAddonSource(addonName, r);
            }
        }

        internal static bool SupportsPackOperation(PackOperation type, string addonName)
        {
            lock (addonName)
            {
                try
                {
                    bool bHasFileList = PseudoBatchFiles.GetFileListName(addonName) != null;
                    bool bHasGlobalNoUnpack = Directory.Exists(GetAddonDirectory(NOUNPACK_TRAILER))
                                              || (Settings.Default.UnpackAddons == false);
                    bool bHasNoUnpack = Directory.Exists(GetAddonDirectory(addonName + NOUNPACK_TRAILER));

                    bool bHasGlobalNoPack = Directory.Exists(GetAddonDirectory(NOPACK_TRAILER));

                    bool bHasNoPack = Directory.Exists(GetAddonDirectory(addonName + NOPACK_TRAILER));

                    if (type == PackOperation.UNPACK && (bHasNoUnpack || bHasGlobalNoUnpack))
                        return false;
                    if (type == PackOperation.PACK && (bHasGlobalNoPack || bHasNoPack))
                        return false;

                    return bHasFileList;
                }
                catch (Exception e)
                {
                    Program.LogDbg(e.ToString());
                }
            }
            return false;
        }

        // Assumes IsInstalled()
        internal static void UninstallAddon(string addonName, TextWriter feedback, ICollection<string> list,
                                            Object oLock)
        {
            lock (addonName)
                _UninstallAddon(addonName, feedback, list, oLock);
        }

        internal static void ValidatePackage(string addonName, TextWriter feedback)
        {
            //lock (addonName)
            //{
            //    // Asumes SupportsPackOperations
            //    using (PseudoBatchFiles p = new PseudoBatchFiles(addonName, feedback))
            //        p.ReapplyTags();
            //}
        }

        internal static bool WowDirectoryExists()
        {
            return (String.IsNullOrEmpty(Settings.Default.WowInstallDirectory) == false) &&
                   Directory.Exists(Settings.Default.WowInstallDirectory);
        }

        internal static void WowInstallDirChanged()
        {
            DirectoryInfo wow = new DirectoryInfo(Settings.Default.WowInstallDirectory);
            if (Settings.Default.WowInstallDirectory != wow.FullName)
                Settings.Default.WowInstallDirectory = wow.FullName;
            AddonsDirectory = null;
        }



        public static bool LoadOutOfDateAddons {
            get
            {
                string resultString = null;
                try
                {
                    string wtf = Path.Combine(FileSystem.CheckPathString(Settings.Default.WowInstallDirectory), "WTF");

                    string subjectString;
                    if (Directory.Exists(wtf))
                        subjectString = File.ReadAllText(Path.Combine(wtf, "Config.wtf"));
                    else
                        return false;

                    resultString = Regex.Match(subjectString, @"SET\s*checkAddonVersion\s*\""([^""]*)\""", RegexOptions.IgnoreCase | RegexOptions.Multiline).Groups[1].Value;
                }
                catch (ArgumentException e)
                {
                    Program.LogDbg(e.ToString());
                }

                if (string.IsNullOrEmpty(resultString) || resultString != "0")
                    return false;
                else
                    return true;

            }
            set
            {
                try
                {
                    string wtf = Path.Combine(FileSystem.CheckPathString(Settings.Default.WowInstallDirectory), "WTF");
                    string subjectString = File.ReadAllText(Path.Combine(wtf, "Config.wtf"));

                    string resultString = Regex.Match(subjectString, @"SET\s*checkAddonVersion\s*\""([^""]*)\""", RegexOptions.IgnoreCase | RegexOptions.Multiline).Groups[1].Value;
                    if (!String.IsNullOrEmpty(resultString))
                    {
                        const string ON = @"${1}0${3}";
                        const string OFF = @"${1}1${3}";

                        string setting = value ? ON : OFF;

                        resultString = Regex.Replace(subjectString, @"(SET\s*checkAddonVersion\s*\"")([^""]*)(\"")",
                           setting, RegexOptions.IgnoreCase | RegexOptions.Multiline);
                    }
                    else
                    {
                        resultString = subjectString + string.Format("SET checkAddonVersion \"{0}\"\r\n", value ? "0" : "1");
                    }

                    File.WriteAllText(Path.Combine(wtf, "Config.wtf"), resultString);
                }
                catch (ArgumentException e)
                {
                    Program.LogDbg(e.ToString());
                }

            }
        }

        internal static string GetWoWVersion()
        {
            try
            {
                DirectoryInfo wtf = new DirectoryInfo(Path.Combine(FileSystem.CheckPathString(Settings.Default.WowInstallDirectory), "WTF"));

                string subjectString;
                if (wtf.Exists)
                    subjectString = File.ReadAllText(Path.Combine(wtf.FullName, "Config.wtf"));
                else
                    return null;

                string resultString = null;
                try
                {
                    resultString = Regex.Match(subjectString, @"SET\s*patchList\s*\""([^""]+)\""", RegexOptions.IgnoreCase | RegexOptions.Multiline).Groups[1].Value;
                }
                catch (ArgumentException e)
                {
                    Program.LogDbg(e.ToString());
                }

                if (string.IsNullOrEmpty(resultString))
                    return null;

                UriBuilder currentversionurl = new UriBuilder(Uri.UriSchemeHttp, resultString);
                currentversionurl.Path = "update/PatchSequenceFile.txt";

                string x;
                using (WebClient w = new WebClient())
                    x = w.DownloadString(currentversionurl.ToString());

                resultString = null;
                try
                {
                    resultString = Regex.Match(x, @"\[GAME\](?:\r?\n(?:[^[\r\n].*)?)*CurrentBuild\s*=\s*(\d+)", RegexOptions.IgnoreCase | RegexOptions.Multiline).Groups[1].Value;
                }
                catch (ArgumentException e)
                {
                    Program.LogDbg(e.ToString());
                }

                return resultString;
            }
            catch (Exception e) { Program.LogDbg(e.ToString()); }

            return null;
        }

        private const string WDN_URL = "http://wdnaddons.com/";

        internal static string GetWoWInterfaceVersion()
        {
            try
            {
                string wowversion = GetWoWVersion();

                if (string.IsNullOrEmpty(wowversion))
                    return null;

                string x;
                using (WebClient w = new WebClient())
                    x = w.DownloadString(WDN_URL);

                if (string.IsNullOrEmpty(x))
                    return null;

                Regex regexObj = new Regex(@"label=""(?:\d\.)+(\d+)"".*build=""(\d+)""", RegexOptions.Singleline | RegexOptions.IgnoreCase);
                Match matchResults = regexObj.Match(x);
                while (matchResults.Success)
                {
                    string clientver = matchResults.Groups[1].Value;
                    string interfacever = matchResults.Groups[2].Value;

                    if (clientver.Contains(wowversion))
                        return interfacever;

                    matchResults = matchResults.NextMatch();
                }
            }
            catch (Exception ex)
            {
                Program.LogDbg(ex.ToString());
            }

            return null;
        }


        #endregion

        #region Private Methods

        private static readonly List<string> EMPTY_LIST = new List<string>(0);

        private static VersionNumber _GetAddonRevision(string addonName)
        {
            try
            {
                string addonDir = GetAddonDirectory(addonName);
                VersionNumber best = VersionNumber.NO_VERSION;

                if (Directory.Exists(addonDir))
                {
                    if (FileSystem.IsSvnWorkingCopy(addonDir) && Settings.Default.SkipSvnWorkingCopies == false)
                    {
                        try
                        {
                            string dname = Path.Combine(addonDir, ".svn" + Path.DirectorySeparatorChar + "dir-props");
                            string fname = Path.Combine(addonDir, ".svn" + Path.DirectorySeparatorChar + "entries");
                            VersionNumber.ExternalsType ext = VersionNumber.ExternalsType.WITHOUT;

                            if (File.Exists(dname))
                            {
                                string[] lines = File.ReadAllLines(dname);

                                int n = Array.IndexOf(lines, "wau:externalsmode");

                                if (n > 0)
                                {
                                    n += 2;
                                    string mode = lines[n];

                                    if (mode == "WITH")
                                        ext = VersionNumber.ExternalsType.WITH;
                                    else
                                        ext = VersionNumber.ExternalsType.WITHOUT;

                                }
                            }

                            if (File.Exists(fname))
                            {
                                string[] lines = File.ReadAllLines(fname);

                                string ver = lines[3];
                                VersionNumber svn = VersionNumber.Parse(ver, ext);

                                return svn;
                            }
                        }
                        catch (Exception e)
                        { Program.LogDbg(e.ToString()); }
                    }

                    rssAddonItem item = GetAddonObject(addonName);

                    if (item != null)
                    {
                        VersionNumber val = WowAceFilesServer.GetAddonRevision(item);

                        if (val > best)
                            best = val;
                    }

                    string[] filePaths = Directory.GetFiles(addonDir, "Changelog*.txt");
                    foreach (string filePath in filePaths)
                    {
                        string filename = Path.GetFileName(filePath);
                        Match m = Regex.Match(filename, "(?<=r)[\\d\\.]+\\d");
                        if (!m.Success)
                            continue;

                        VersionNumber val = VersionNumber.Parse(m.ToString());
                        if (val > best)
                            best = val;
                    }
                }
                //Program.LogDbg("Local Version " + best);
                return best;
            }
            catch
            {
                return VersionNumber.NO_VERSION;
            }
        }

        private static void _InstallAddon(string zipFile, string addonName, rssAddonItem addonSource,
                                          TextWriter feedback, ICollection<string> list, Object oLock)
        {
            try
            {
                using (new TextWriterMessageWrapper("\tExtracting ZIP file...", feedback))
                    FileSystem.UnzipAll(zipFile, AddonsDirectory);

                if (!IsInstalled(addonName))
                    throw new FileNotFoundException("The addon was not unzipped to the expected location.");

                SaveAddonSource(addonSource);

                StreamWriter sw = null;
                using (ZipFile z = new ZipFile(zipFile))
                try
                {
                    sw = new StreamWriter(
                        Path.Combine(GetAddonDirectory(addonName), ZIP_INVENTORY_FILE));

                    foreach (ZipEntry f in z)
                    {
                        sw.WriteLine(f.Name);
                    }
                }
                finally {
                    if (sw != null)
                        sw.Close();
                }

                //                VersionNumber v = WowAceFilesServer.GetAddonRevision(addonSource);

                //if (v.Externals == VersionNumber.ExternalsType.WITHOUT && File.Exists(EmbedsFile(addonName)))
                //    using (new TextWriterMessageWrapper("\tRemoving Embedded Library References...", feedback))
                //        EmptyXMLFileContents(EmbedsFile(addonName));

                if (SupportsPackOperation(PackOperation.UNPACK, addonName))
                {
                    using (new TextWriterMessageWrapper(Resources.UnpackingAddonContents, feedback))
                    using (PseudoBatchFiles p = new PseudoBatchFiles(addonName, feedback))
                    {
                        List<string> changedMods = p.Install();
                        lock (oLock)
                        {
                            foreach (string s in changedMods)
                            {
                                if (WowAceFilesServer.FilesLookup.ContainsKey(s))
                                {
                                    Program.LogDbg("Queuing: " + s);
                                    list.Add(s);
                                }
                            }
                        }
                    }

                    if (File.Exists(Path.Combine(GetAddonDirectory(addonName), "modules.xml")))
                        using (new TextWriterMessageWrapper(Resources.RemovingReferencesToUnpackedModules, feedback))
                            EmptyXMLFileContents(ModulesFile(addonName));
                }
            }
            catch (Exception e)
            {
                feedback.WriteLine("FAILED: " + e.Message);
                throw;
            }
        }


        private static void _RepackAddon(string addonName, TextWriter feedback,
                                         ICollection<string> changedList, ICollection<string> filesInNewVersion,
                                         Object oLock, bool bPreserveContents)
        {
            if (SupportsPackOperation(PackOperation.PACK, addonName))
            {
                List<string> changedMods = null;
                try
                {
                    feedback.WriteLine(Resources.Repacking0, addonName);
                    using (PseudoBatchFiles p = new PseudoBatchFiles(addonName, feedback))
                        changedMods = p.Uninstall(oLock);
                }
                catch (Exception e)
                {
                    Program.LogDbg(e.ToString());
                }

                if (changedMods != null)
                    lock (oLock)
                    {
                        foreach (string s in changedMods)
                            if (WowAceFilesServer.FilesLookup.ContainsKey(s))
                                changedList.Add(s);
                    }
            }

            DirectoryInfo addonDir = new DirectoryInfo(GetAddonDirectory(addonName));
            string oldContentsFile = Path.Combine(addonDir.FullName, ZIP_INVENTORY_FILE);

            if (bPreserveContents && File.Exists(oldContentsFile))
            {
                List<string> oldContents = new List<string>(File.ReadAllLines(oldContentsFile));
                List<string> oldContentsMask = new List<string>();

                foreach (string f in oldContents)
                    if (filesInNewVersion.Contains(f))
                        oldContentsMask.Add(f);
                    else
                        try
                        {
                            string p = Path.Combine(AddonsDirectory, f);

                            Program.LogDbg(string.Format("{0}: content {1} -> removing", addonName, f));

                            if (Directory.Exists(Path.GetDirectoryName(p)))
                                File.Delete(p);
                        }
                        catch (Exception e)
                        {
                            Program.LogDbg(e.ToString());
                        }

                foreach (string f in filesInNewVersion)
                    if (!oldContentsMask.Contains(f))
                        Program.LogDbg(string.Format("{0}: content {1} is new", addonName, f));

                List<string> dirList = new List<string>();
                foreach (DirectoryInfo d in addonDir.GetDirectories("*", SearchOption.AllDirectories))
                    dirList.Add(d.FullName);

                dirList.Sort();
                dirList.Reverse();


                foreach (string d in dirList)
                    if (Directory.GetFileSystemEntries(d).Length == 0)
                        try
                        {
                            Directory.Delete(d);
                        }
                        catch (Exception e)
                        {
                            Program.LogDbg(e.ToString());
                        }
            }

            lock (oLookLock)
            {
                            LocalLookup[addonName] = null;
                            LocalProviderAddon = null;
            }

            if (bPreserveContents && Directory.Exists(addonDir.FullName))
            {
                File.Delete(Path.Combine(addonDir.FullName, ADDON_SOURCE_INFO_FILE));
                
                File.Delete(oldContentsFile);
            }
        }


        private static void _UninstallAddon(string addonName, TextWriter feedback, ICollection<string> list,
                                            Object oLock)
        {
            try
            {
                feedback.WriteLine(Resources.Uninstalling0, addonName);
                RepackAddon(addonName, feedback, list, EMPTY_LIST, oLock, false);

                FileSystem.DeleteDirectory(GetAddonDirectory(addonName));
            }
            catch (Exception e)
            {
                feedback.WriteLine("FAILED" + e.Message);
            }
        }

        private static void EmptyXMLFileContents(string xmlFile)
        {
            try
            {
                XmlDocument x = new XmlDocument();

                using (XmlReader xr = XmlReader.Create(xmlFile))
                    x.Load(xr);
                XmlNode ui = x.DocumentElement;

                if (ui != null)
                    while (ui.ChildNodes.Count > 0)
                        ui.RemoveChild(ui.FirstChild);

                using (XmlWriter xw = XmlWriter.Create(xmlFile))
                    x.WriteTo(xw);
            }
            catch (Exception e)
            {
                Program.LogDbg(e.ToString());
            }
        }

        #endregion

        //         public static string EmbedsFile(string addonName)
        //         {
        //             return Path.Combine(GetAddonDirectory(addonName), "embeds.xml");
        //         }
    }
}